/*
 * $Id: BasicTipOfTheDayUI.java 3927 2011-02-22 16:34:11Z kleopatra $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.plaf.basic;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Locale;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.html.HTMLDocument;

import org.jdesktop.swingx.JXTipOfTheDay;
import org.jdesktop.swingx.JXTipOfTheDay.ShowOnStartupChoice;
import org.jdesktop.swingx.SwingXUtilities;
import org.jdesktop.swingx.plaf.TipOfTheDayUI;
import org.jdesktop.swingx.plaf.UIManagerExt;
import org.jdesktop.swingx.tips.TipOfTheDayModel.Tip;

/**
 * Base implementation of the <code>JXTipOfTheDay</code> UI.
 * 
 * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
 */
public class BasicTipOfTheDayUI extends TipOfTheDayUI {

	public static ComponentUI createUI(JComponent c) {
		return new BasicTipOfTheDayUI((JXTipOfTheDay) c);
	}

	protected JXTipOfTheDay tipPane;
	protected JPanel tipArea;
	protected Component currentTipComponent;

	protected Font tipFont;
	protected PropertyChangeListener changeListener;

	public BasicTipOfTheDayUI(JXTipOfTheDay tipPane) {
		this.tipPane = tipPane;
	}

	@Override
	public JDialog createDialog(Component parentComponent, final ShowOnStartupChoice choice) {
		return createDialog(parentComponent, choice, true);
	}

	protected JDialog createDialog(Component parentComponent, final ShowOnStartupChoice choice, boolean showPreviousButton) {
		Locale locale = parentComponent == null ? null : parentComponent.getLocale();
		String title = UIManagerExt.getString("TipOfTheDay.dialogTitle", locale);

		final JDialog dialog;

		Window window;
		if (parentComponent == null) {
			window = JOptionPane.getRootFrame();
		} else {
			window = (parentComponent instanceof Window) ? (Window) parentComponent : SwingUtilities
					.getWindowAncestor(parentComponent);
		}

		if (window instanceof Frame) {
			dialog = new JDialog((Frame) window, title, true);
		} else {
			dialog = new JDialog((Dialog) window, title, true);
		}

		dialog.getContentPane().setLayout(new BorderLayout(10, 10));
		dialog.getContentPane().add(tipPane, BorderLayout.CENTER);
		((JComponent) dialog.getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		final JCheckBox showOnStartupBox;

		// tip controls
		JPanel controls = new JPanel(new BorderLayout());
		dialog.add("South", controls);

		if (choice != null) {
			showOnStartupBox = new JCheckBox(UIManagerExt.getString("TipOfTheDay.showOnStartupText", locale),
					choice.isShowingOnStartup());
			controls.add(showOnStartupBox, BorderLayout.CENTER);
		} else {
			showOnStartupBox = null;
		}

		JPanel buttons = new JPanel(new GridLayout(1, showPreviousButton ? 3 : 2, 9, 0));
		controls.add(buttons, BorderLayout.LINE_END);

		if (showPreviousButton) {
			JButton previousTipButton = new JButton(UIManagerExt.getString("TipOfTheDay.previousTipText", locale));
			buttons.add(previousTipButton);
			previousTipButton.addActionListener(getActionMap().get("previousTip"));
		}

		JButton nextTipButton = new JButton(UIManagerExt.getString("TipOfTheDay.nextTipText", locale));
		buttons.add(nextTipButton);
		nextTipButton.addActionListener(getActionMap().get("nextTip"));

		JButton closeButton = new JButton(UIManagerExt.getString("TipOfTheDay.closeText", locale));
		buttons.add(closeButton);

		final ActionListener saveChoice = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (choice != null) {
					choice.setShowingOnStartup(showOnStartupBox.isSelected());
				}
				dialog.setVisible(false);
			}
		};

		closeButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				dialog.setVisible(false);
				saveChoice.actionPerformed(null);
			}
		});
		dialog.getRootPane().setDefaultButton(closeButton);

		dialog.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				saveChoice.actionPerformed(null);
			}
		});

		((JComponent) dialog.getContentPane()).registerKeyboardAction(saveChoice,
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);

		dialog.pack();
		dialog.setLocationRelativeTo(parentComponent);

		return dialog;
	}

	@Override
	public void installUI(JComponent c) {
		super.installUI(c);
		installDefaults();
		installKeyboardActions();
		installComponents();
		installListeners();

		showCurrentTip();
	}

	protected void installKeyboardActions() {
		ActionMap map = getActionMap();
		if (map != null) {
			SwingUtilities.replaceUIActionMap(tipPane, map);
		}
	}

	ActionMap getActionMap() {
		ActionMap map = new ActionMapUIResource();
		map.put("previousTip", new PreviousTipAction());
		map.put("nextTip", new NextTipAction());
		return map;
	}

	protected void installListeners() {
		changeListener = createChangeListener();
		tipPane.addPropertyChangeListener(changeListener);
	}

	protected PropertyChangeListener createChangeListener() {
		return new ChangeListener();
	}

	protected void installDefaults() {
		LookAndFeel.installColorsAndFont(tipPane, "TipOfTheDay.background", "TipOfTheDay.foreground", "TipOfTheDay.font");
		LookAndFeel.installBorder(tipPane, "TipOfTheDay.border");
		LookAndFeel.installProperty(tipPane, "opaque", Boolean.TRUE);
		tipFont = UIManager.getFont("TipOfTheDay.tipFont");
	}

	protected void installComponents() {
		tipPane.setLayout(new BorderLayout());

		// tip icon
		JLabel tipIcon = new JLabel(UIManagerExt.getString("TipOfTheDay.didYouKnowText", tipPane.getLocale()));
		tipIcon.setIcon(UIManager.getIcon("TipOfTheDay.icon"));
		tipIcon.setBorder(BorderFactory.createEmptyBorder(22, 15, 22, 15));
		tipPane.add("North", tipIcon);

		// tip area
		tipArea = new JPanel(new BorderLayout(2, 2));
		tipArea.setOpaque(false);
		tipArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
		tipPane.add("Center", tipArea);
	}

	@Override
	public Dimension getPreferredSize(JComponent c) {
		return new Dimension(420, 175);
	}

	protected void showCurrentTip() {
		if (currentTipComponent != null) {
			tipArea.remove(currentTipComponent);
		}

		int currentTip = tipPane.getCurrentTip();
		if (currentTip == -1) {
			JLabel label = new JLabel();
			label.setOpaque(true);
			label.setBackground(UIManager.getColor("TextArea.background"));
			currentTipComponent = label;
			tipArea.add("Center", currentTipComponent);
			return;
		}

		// tip does not fall in current tip range
		if (tipPane.getModel() == null || tipPane.getModel().getTipCount() == 0
				|| (currentTip < 0 && currentTip >= tipPane.getModel().getTipCount())) {
			currentTipComponent = new JLabel();
		} else {
			Tip tip = tipPane.getModel().getTipAt(currentTip);

			Object tipObject = tip.getTip();
			if (tipObject instanceof Component) {
				currentTipComponent = (Component) tipObject;
			} else if (tipObject instanceof Icon) {
				currentTipComponent = new JLabel((Icon) tipObject);
			} else {
				JScrollPane tipScroll = new JScrollPane();
				tipScroll.setBorder(null);
				tipScroll.setOpaque(false);
				tipScroll.getViewport().setOpaque(false);
				tipScroll.setBorder(null);

				String text = tipObject == null ? "" : tipObject.toString();

				if (BasicHTML.isHTMLString(text)) {
					JEditorPane editor = new JEditorPane("text/html", text);
					editor.setFont(tipPane.getFont());
					// BasicHTML.updateRenderer(editor, text);
					SwingXUtilities.setHtmlFont((HTMLDocument) editor.getDocument(), tipPane.getFont());
					editor.setEditable(false);
					editor.setBorder(null);
					editor.setMargin(null);
					editor.setOpaque(false);
					tipScroll.getViewport().setView(editor);
				} else {
					JTextArea area = new JTextArea(text);
					area.setFont(tipPane.getFont());
					area.setEditable(false);
					area.setLineWrap(true);
					area.setWrapStyleWord(true);
					area.setBorder(null);
					area.setMargin(null);
					area.setOpaque(false);
					tipScroll.getViewport().setView(area);
				}

				currentTipComponent = tipScroll;
			}
		}

		tipArea.add("Center", currentTipComponent);
		tipArea.revalidate();
		tipArea.repaint();
	}

	@Override
	public void uninstallUI(JComponent c) {
		uninstallListeners();
		uninstallComponents();
		uninstallDefaults();
		super.uninstallUI(c);
	}

	protected void uninstallListeners() {
		tipPane.removePropertyChangeListener(changeListener);
	}

	protected void uninstallComponents() {
	}

	protected void uninstallDefaults() {
	}

	class ChangeListener implements PropertyChangeListener {
		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			if (JXTipOfTheDay.CURRENT_TIP_CHANGED_KEY.equals(evt.getPropertyName())) {
				showCurrentTip();
			}
		}
	}

	class PreviousTipAction extends AbstractAction {
		public PreviousTipAction() {
			super("previousTip");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			tipPane.previousTip();
		}

		@Override
		public boolean isEnabled() {
			return tipPane.isEnabled();
		}
	}

	class NextTipAction extends AbstractAction {
		public NextTipAction() {
			super("nextTip");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			tipPane.nextTip();
		}

		@Override
		public boolean isEnabled() {
			return tipPane.isEnabled();
		}
	}

}
